/*
 * Copyright 2013 Brandon O'Toole
 *
 * This file is part of EdgeSleuth.
 *
 * EdgeSleuth is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * EdgeSleuth is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EdgeSleuth.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <jni.h>
#include <math.h>

float selfMultiply( float );
float cnComputeDIntensity(jint c, jint c0);
int cnComputeColor(jint inten);
void cnFindEdges(jint *s, jsize s_len, jint *c, jsize c_len, jint *o, jsize o_len, jint o_h, jint o_w);
void cnLoopCompute(jint *color, jsize c_len, jint *intensity, jsize i_len);
int cnCalculateEdgeStrength(int index, jint *o, jint o_h, jint o_w);


JNIEXPORT void JNICALL 
        Java_com_IndieRayDev_EdgeSleuth_EdgeDetector_cFindEdges(
        JNIEnv * env, jobject  obj, 
        jintArray strength, jintArray color, jintArray original, jint o_h, jint o_w){

    jsize s_len = (*env)->GetArrayLength(env, strength);
    jsize c_len = (*env)->GetArrayLength(env, color);
    jsize o_len = (*env)->GetArrayLength(env, original);
    jint *s = (*env)->GetIntArrayElements(env, strength, 0);
    jint *c = (*env)->GetIntArrayElements(env, color, 0);
    jint *o = (*env)->GetIntArrayElements(env, original, 0);

    cnFindEdges(s, s_len, c, c_len, o, o_len, o_h, o_w);

    (*env)->ReleaseIntArrayElements(env, strength, s, 0);
    (*env)->ReleaseIntArrayElements(env, color, c, 0);
    (*env)->ReleaseIntArrayElements(env, original, o, 0);

    return;

}
void cnFindEdges(jint *s, jsize s_len, jint *c, jsize c_len, jint *o, jsize o_len, jint o_h, jint o_w){
    int i, j, k;
    int oLenEnd = o_w* (o_h-10);

    for( i=10*o_w; i<oLenEnd; i++){
        s[i] = cnCalculateEdgeStrength(i, o, o_h, o_w);
    }

    for( i=0; i<o_w; i++){
        for( k=0; k<10; k++){
            s[i+k*o_w] = 0;
            s[i+(o_h-1) - k] = 0;
        }
    }

    for( j=0; j<o_h; j++){
        for( k=0; k<10; k++){
            s[j*o_w + k] = 0;
            s[(j+1)*o_w - 1 - k] = 0;
        }
    }
			
    // replaced with jni call
    cnLoopCompute(c, c_len, s, s_len);

    return;
}


JNIEXPORT int JNICALL 
        Java_com_IndieRayDev_EdgeSleuth_EdgeDetector_cCalculateEdgeStrength(
        JNIEnv * env, jobject  obj, 
        jint index, jintArray original, jint o_h, jint o_w){

    jsize o_len = (*env)->GetArrayLength(env, original);
    jint *o = (*env)->GetIntArrayElements(env, original, 0);

    int output = cnCalculateEdgeStrength(index, o, o_h, o_w);

    (*env)->ReleaseIntArrayElements(env, original, o, 0);

    return output;
}
int cnCalculateEdgeStrength(int index, jint *o, int o_h, int o_w){
    
    int r = 1;	
    //nColor = o[index - r * o_h];
    //sColor = o[index + r * o_w];
    //eColor = o[index + r]; 
    //wColor = o[index - r];
    
    return (int)floor((double)sqrtf(
            selfMultiply(cnComputeDIntensity(   o[index - r * o_w]   ,    o[index + r * o_w]   )) + 
            selfMultiply(cnComputeDIntensity(   o[index + r]         ,    o[index - r]         ))
            ));
}



JNIEXPORT int JNICALL 
        Java_com_IndieRayDev_EdgeSleuth_EdgeDetector_cComputeColor(
        JNIEnv * env, jobject  obj, jint inten){

    return cnComputeColor(inten);
}
int cnComputeColor(jint inten){
    return (
            (
            ( (inten&0xff)       ) + 
            ( (inten&0xff) << 8  ) + 
            ( (inten&0xff) << 16 )   
            ) | (0xff000000)
            );
}



JNIEXPORT float JNICALL 
        Java_com_IndieRayDev_EdgeSleuth_EdgeDetector_cComputeDIntensity(
        JNIEnv * env, jobject  obj, jint c, jint c0){

    return cnComputeDIntensity(c, c0);
}
float cnComputeDIntensity(jint c, jint c0){
    return sqrtf(
            selfMultiply(   (float)( ((c>>16)&0xff) - ((c0>>16)&0xff) )   ) + 
            selfMultiply(   (float)( ((c>> 8)&0xff) - ((c0>> 8)&0xff) )   ) + 
            selfMultiply(   (float)( ((c    )&0xff) - ((c0    )&0xff) )   )    );
}



JNIEXPORT void JNICALL 
        Java_com_IndieRayDev_EdgeSleuth_EdgeDetector_loopCompute(
        JNIEnv * env, jobject  obj, jintArray inArray, jintArray outArray){

    jsize c_len = (*env)->GetArrayLength(env, inArray);
    jsize i_len = (*env)->GetArrayLength(env, outArray);
    jint *intensity = (*env)->GetIntArrayElements(env, inArray, 0);
    jint *color = (*env)->GetIntArrayElements(env, outArray, 0);
 
    cnLoopCompute(color, c_len, intensity, i_len);

    (*env)->ReleaseIntArrayElements(env, inArray, intensity, 0);
    (*env)->ReleaseIntArrayElements(env, outArray, color, 0);
    
    return;
}
void cnLoopCompute(jint *color, jsize c_len, jint *intensity, jsize i_len){
    int i = 0;
    for( i=0; i<c_len; i++){
        color[i] = cnComputeColor(intensity[i]);
    }
}



float selfMultiply(float self){
    return self*self;
}
